package model

import (
	"encoding/json"
	"fmt"
	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/assert"
	"go.mongodb.org/mongo-driver/bson"
	"testing"
)

type (
	Suit struct {
		Value    interface{}
		Expected string
	}
)

func TestJsonSuits(t *testing.T, suits []Suit, unmarshall func([]byte) (interface{}, error)) {
	for i, current := range suits {
		t.Run(fmt.Sprintf("scenario-%d", i), test_json(current, unmarshall))
	}
}

func TestBsonSuits(t *testing.T, suits []Suit, unmarshall func(bson.RawValue) (interface{}, error)) {
	for i, current := range suits {
		t.Run(fmt.Sprintf("scenario-%d", i), test_bson(current, unmarshall))
	}
}

func test_json(current Suit, unmarshall func([]byte) (interface{}, error)) func(*testing.T) {
	return func(t *testing.T) {
		actual, err := json.Marshal(current.Value)
		if err != nil {
			t.Fatalf("failed to marshal '%v': %s", current.Value, err)
		}

		assert.Equal(t, current.Expected, string(actual))

		reverseActual, err := unmarshall(actual)
		if err != nil {
			t.Fatalf("failed to unmarshall '%s': %s", string(actual), err)
		}

		equal := cmp.Equal(current.Value, reverseActual)
		if !equal {
			t.Errorf("not equal:\n %s", cmp.Diff(current.Value, reverseActual))
		}

	}
}

func test_bson(current Suit, unmarshall func(bson.RawValue) (interface{}, error)) func(*testing.T) {
	return func(t *testing.T) {
		actualType, actual, err := bson.MarshalValue(current.Value)
		if err != nil {
			t.Fatalf("failed to marshal '%v': %s", current.Value, err)
		}

		assert.Equal(t, current.Expected, bson.RawValue{Type: actualType, Value: actual}.String())

		raw := bson.RawValue{Type: actualType, Value: actual}

		reverseActual, err := unmarshall(raw)
		if err != nil {
			t.Fatalf("failed to unmarshall '%s': %s", raw.String(), err)
		}

		assert.Equal(t, current.Value, reverseActual)
	}
}
